import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:cross_file/cross_file.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:get/get.dart';
import 'package:hive/hive.dart';
import 'package:http/http.dart' as http;
import 'package:http_parser/http_parser.dart';
import 'package:record/record.dart';

import '../../constants.dart';
import '../../controller/cache.dart';
import 'SampleAudioPlayer.dart';

final _audioRecorder = Record();
RxString _localPath = ''.obs;
RxBool _isRecording = false.obs;
RxBool _isPaused = false.obs;
RxBool _isShowSave = false.obs;
RxInt _recordDuration = 0.obs;
RxBool _isShowPlayer = false.obs;
Timer? _timer;
RxString _audioId = ''.obs;
RxBool _isShowCompleted = false.obs;

/* BGRecorder 使用说明
    *  @required  getFileId        保存成功后获取fileId
    *  @option    audioUrl         player的播放地址,支持url、localPath、fileId
    *             getLocalPath     获取录音本地存储路径
    *             getDeleteAction  获取删除事件
    * */
// ignore: must_be_immutable
class BGRecorder extends StatefulWidget {
  BGRecorder(
      {Key? key,
      this.audioUrl,
      required this.getFileId,
      this.getLocalPath,
      this.getDeleteAction})
      : super(key: key);
  String? audioUrl;
  Function getFileId;
  Function? getLocalPath;
  Function? getDeleteAction;

  @override
  State<BGRecorder> createState() => _BGRecorderState();
}

class _BGRecorderState extends State<BGRecorder>
    with SingleTickerProviderStateMixin {
  @override
  Widget build(BuildContext context) {
    setDefaultStatus();
    String? audioUrl = widget.audioUrl;
    if (audioUrl != null && audioUrl.isNotEmpty) {
      if (audioUrl.endsWith('.m4a') || audioUrl.startsWith('blob:http')) {
        _localPath.value = audioUrl;
        _isShowCompleted.value = false;
      } else {
        _audioId.value = audioUrl;
        _isShowCompleted.value = true;
      }
      print('audioUrl: $audioUrl');
      _stopRecordState();
    }

    return Container(
        padding: const EdgeInsets.all(15),
        width: MediaQuery.of(Get.context!).size.width,
        height: 200,
        decoration: BoxDecoration(
            color: Color.fromARGB(209, 236, 241, 241),
            borderRadius: BorderRadius.all(
              Radius.circular(10),
            )),
        child: Container(
            child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            Row(mainAxisAlignment: MainAxisAlignment.center, children: [
              Obx(() {
                return _buildRecordStopControl(
                    _isRecording.value, _isPaused.value, _isShowSave.value);
              }),
              Obx(() {
                return _buildSaveControl(
                    _isShowSave.value, _isShowCompleted.value);
              }),
              Obx(() {
                return _buildDeleteControl(_isShowSave.value);
              }),
              Obx(() {
                return _buildPauseResumeControl(
                    _isRecording.value, _isPaused.value);
              }),
              Obx(() {
                return _buildTimer(_recordDuration.value.toInt(),
                    (_isRecording.value == true || _isPaused.value == true));
              })
            ]),
            SizedBox(height: 20),
            Obx(() {
              return _buildPlayerWidget(_localPath.value, _audioId.value);
            })
          ],
        )));
  }

  ///     ************* Widgets  *************
  Widget _buildRecordStopControl(isRecording, isPaused, bool isShowSave) {
    if (isShowSave && !isRecording && !isPaused) return SizedBox();
    late Icon icon;
    late Color color;
    if (isRecording == true || isPaused == true) {
      icon = const Icon(Icons.stop, color: Colors.red, size: 30);
      color = Colors.red.withOpacity(0.1);
    } else {
      icon =
          Icon(Icons.mic, color: Color.fromARGB(255, 98, 191, 240), size: 30);
      color = Color.fromARGB(255, 98, 191, 240).withOpacity(0.1);
    }

    return ClipOval(
      child: Material(
        color: color,
        child: InkWell(
          child: SizedBox(width: 56, height: 56, child: icon),
          onTap: () {
            isRecording == true ? _stop() : _start();
          },
        ),
      ),
    );
  }

  Widget _buildSaveControl(bool isShowSave, bool isShowCompleted) {
    if (!isShowSave) return SizedBox();
    late Icon icon;
    late Color color;

    Color showColor =
        isShowCompleted ? Color.fromARGB(255, 98, 191, 240) : Colors.red;

    icon = Icon(Icons.save_outlined, color: showColor, size: 30);
    color = showColor.withOpacity(0.1);

    return Row(
      children: [
        SizedBox(width: 20),
        ClipOval(
          child: Material(
            color: color,
            child: InkWell(
              child: SizedBox(width: 56, height: 56, child: icon),
              onTap: () {
                if (isShowCompleted) {
                  EasyLoading.show();
                  Future.delayed(const Duration(milliseconds: 1000), () {
                    EasyLoading.dismiss();
                    Get.snackbar('Message'.tr, 'Saved Successfully'.tr,
                        snackPosition: SnackPosition.BOTTOM);
                  });
                } else {
                  _save();
                }
              },
            ),
          ),
        ),
        SizedBox(width: 20)
      ],
    );
  }

  Widget _buildDeleteControl(bool isShowSave) {
    if (!isShowSave) return SizedBox();
    late Icon icon;
    late Color color;
    icon = Icon(Icons.delete_outline,
        color: Color.fromARGB(255, 98, 191, 240), size: 30);
    color = Color.fromARGB(255, 98, 191, 240).withOpacity(0.1);

    return Row(children: [
      ClipOval(
        child: Material(
          color: color,
          child: InkWell(
            child: SizedBox(width: 56, height: 56, child: icon),
            onTap: () {
              _delete();
            },
          ),
        ),
      ),
      SizedBox(width: 25)
    ]);
  }

  Widget _buildPauseResumeControl(isRecording, isPaused) {
    if (isRecording == false && isPaused == false) {
      return const SizedBox();
    }

    late Icon icon;
    late Color color;

    if (isPaused) {
      icon = const Icon(Icons.play_arrow, color: Colors.red, size: 30);
      color = Color.fromARGB(255, 98, 191, 240).withOpacity(0.1);
    } else {
      icon = const Icon(Icons.pause, color: Colors.red, size: 30);
      color = Colors.red.withOpacity(0.1);
    }

    return Row(
      children: [
        SizedBox(width: 20),
        ClipOval(
          child: Material(
            color: color,
            child: InkWell(
              child: SizedBox(width: 56, height: 56, child: icon),
              onTap: () {
                isPaused == true ? _resume() : _pause();
              },
            ),
          ),
        ),
        SizedBox(width: 20)
      ],
    );
  }

  Widget _buildTimer(recordDuration, bool isShowTime) {
    if (!isShowTime) return SizedBox();
    final String minutes = _formatNumber(recordDuration ~/ 60);
    final String seconds = _formatNumber(recordDuration % 60);
    return ClipOval(
      child: Material(
        child: Container(
          height: 40,
          width: 80,
          alignment: Alignment.center,
          child: Text(
            '$minutes : $seconds',
            style: const TextStyle(
              color: Colors.red,
            ),
          ),
        ),
      ),
    );
  }

  String _formatNumber(int number) {
    String numberStr = number.toString();
    if (number < 10) {
      numberStr = '0' + numberStr;
    }
    return numberStr;
  }

  Widget _buildPlayerWidget(String audioUrl, String audioId) {
    if (_isShowPlayer.value == true) {
      return Container(
          padding: const EdgeInsets.all(8),
          decoration: BoxDecoration(
            color: Color.fromARGB(50, 167, 169, 170),
            borderRadius: BorderRadius.all(Radius.circular(10)),
          ),
          child: SampleAudioPlayer(
              name: 'audioPlayerExam', audioUrl: audioUrl, audioId: audioId));
    } else {
      return SizedBox();
    }
  }

  ///     ************* recorder  *************
  /*
       * AudioEncoder各平台音频支持类型
        | Encoder         | Android        | iOS     | web     | Windows | macOS   | linux
        |-----------------|----------------|---------|---------|---------|---------|---------
        | aacLc           | ✔️             |   ✔️    |  ?      |   ✔️    |  ✔️    |  ✔️
        | aacEld          | ✔️             |   ✔️    |  ?      |         |  ✔️    |
        | aacHe           | ✔️             |   ✔️    |  ?      |   ✔️    |  ✔️    |  ✔️
        | amrNb           | ✔️             |   ✔️    |  ?      |         |  ✔️    |
        | amrWb           | ✔️             |   ✔️    |  ?      |         |  ✔️    |
        | opus            | ✔️             |   ✔️    |  ?      |   ✔️    |  ✔️    |  ✔️
        | vorbisOgg       | ?(optional)    |         |  ?      |   ✔️    |        |   ✔️
        | wav             | ✔️             |         |  ?      |   ✔️    |        |  ✔️
        | flac            |                |   ✔️    |  ?      |   ✔️    |  ✔️    |  ✔️
        | pcm8bit         | ✔️             |   ✔️    |  ?      |         |  ✔️    |
        | pcm16bit        | ✔️             |   ✔️    |  ?      |         |  ✔️    |
       *
       * 音频的计算公式： （时间（秒）×采样频率（赫兹hz）×采样位数(bit)×声道数）/（8×1024×1024）MB
       * 举例：1分钟采样位数为16位、采样频率为44.1Khz的立体声（双声道）不压缩的数据量约为10.09MB。
       *      60 × 44100 × 16 × 2 /(8×1024×1024) = 10.09MB
       * 科大讯飞提供三种模式
       * 1.语音转写
       * 音频属性 采样率16k或8k、位长8bit或16bit、单声道&多声道
       * 音频格式 wav/flac/opus/m4a/mp3
       * 音频时长	不超过5小时，建议5分钟以上
       * 音频时长X（分钟）	参考返回时间Y（分钟）
            X<10	          Y<3
         10<=X<30       	3<=Y<6
         30<=X<60	        6<=Y<10
           60<=X	        10<=Y<20
       * 2.极速语音转写
       * 音频属性 采样率16k、位长16、单声道
       * 音频格式 wav/pcm/mp3
       * 音频时长	不超过5小时，建议5分钟以上
       * 转换效率 20秒内
       *
       * 3.实时语音转写
       * 音频属性 采样率为16K，采样深度为16bit的pcm_s16le音频
       *
       * encoder: AudioEncoder.aacLc, // by default
       * bitRate: 128000, // 音频比特率,单位:bit
       * samplingRate: 44100, //音频采样率，单位：赫兹 hz
       * numChannels: 2 //声道数（1 = 单声道，2 = 双声道（立体声））
       * */

  ///     ************* Recorder Actions  *************
  Future<void> _start() async {
    try {
      if (await _audioRecorder.hasPermission()) {
        await _audioRecorder.start(
            samplingRate: 16000, bitRate: 24000, numChannels: 1);
        await _audioRecorder.isRecording();
        _isRecording.value = true;
        _isShowSave.value = false;
        _setRecordTime();
      }
    } catch (e) {
      if (kDebugMode) {
        print(e);
      }
    }
  }

  Future<void> _pause() async {
    await _audioRecorder.pause();
    _timer?.cancel();
    _isPaused.value = true;
  }

  Future<void> _stop() async {
    final path = await _audioRecorder.stop();
    print('stop in controller $path');
    _localPath.value = path!;
    widget.getLocalPath!(path);
    _isShowCompleted.value = false;
    _stopRecordState();
  }

  void _stopRecordState() {
    _timer?.cancel();
    _isRecording.value = false;
    _isShowSave.value = true;
    _isShowPlayer.value = true;
    _recordDuration.value = 0;
    _isPaused.value = false;
  }

  Future<void> _resume() async {
    _setRecordTime();
    await _audioRecorder.resume();
    _isPaused.value = false;
  }

  void _setRecordTime() {
    _timer?.cancel();
    _timer = Timer.periodic(const Duration(seconds: 1), (Timer t) {
      _recordDuration.value++;
    });
  }

  ///     ************* Widgets Actions  *************

  Future<void> _save() async {
    EasyLoading.show();
    String fileId = await _saveRecordFile();
    EasyLoading.dismiss();
    if (fileId.isNotEmpty) {
      _isShowCompleted.value = true;
      Get.snackbar('Message'.tr, 'Saved Successfully'.tr,
          snackPosition: SnackPosition.TOP);
      widget.getFileId(fileId);
    } else {
      Get.snackbar('Message'.tr, 'Error to save'.tr,
          snackPosition: SnackPosition.TOP);
    }
  }

  Future<String> _saveRecordFile() async {
    final box = Hive.box('appconfig');
    var _authtoken = box.get(CacheManagerKey['authtoken'].toString());
    var uri = Uri.parse('${Constants.BaseAPI}studentmanager/file');
    var request = http.MultipartRequest('POST', uri);
    request.headers.addAll({'authtoken': _authtoken});

    try {
      late var bytes;
      if (_localPath.value.startsWith('blob:http')) {
        bytes = await XFile(_localPath.value).readAsBytes();
      } else {
        bytes = await File.fromUri(Uri.parse(_localPath.value)).readAsBytes();
      }
      request.files.add(http.MultipartFile.fromBytes('file', bytes,
          filename: 'file', contentType: MediaType('audio', 'mpeg')));
      var response = await request.send();
      final respStr = await response.stream.bytesToString();
      var fileId = jsonDecode(respStr)['fileId'];
      if (fileId != null) {
        return fileId;
      }
    } catch (error) {
      print(error);
    }
    return '';
  }

  Future<void> _delete() async {
    Get.defaultDialog(
      backgroundColor: Get.isDarkMode
          ? (Color(0xff333333))
          : Color.fromARGB(255, 234, 241, 238),
      title: 'WARNING'.tr,
      content: Container(
        width: 360,
        padding: EdgeInsets.all(20),
        child: Text(
          'Are you sure you want to '.tr + 'Delete'.tr + '?',
          style: TextStyle(letterSpacing: 1, fontSize: 20),
        ),
      ),
      onCancel: () => null,
      onConfirm: () {
        setDefaultStatus();
        widget.getDeleteAction!();
        Get.back();
      },
    );
  }

  void setDefaultStatus() {
    _localPath.value = '';
    _isRecording.value = false;
    _isPaused.value = false;
    _isShowSave.value = false;
    _recordDuration.value = 0;
    _isShowPlayer.value = false;
    _timer?.cancel();
    _audioId.value = '';
    _isShowCompleted.value = false;
  }

  @override
  void dispose() {
    setDefaultStatus();
    _audioRecorder.dispose();
    super.dispose();
  }
}
